Skip to content

feat(databricks): one Genie space across deploying identities (parentPath); plan, diff and generate iac show it - #869

Merged
fas89 merged 9 commits into
mainfrom
feat/databricks-genie-shared-folder
Nov 12, 2026
Merged

fas89 merged 9 commits into
mainfrom
feat/databricks-genie-shared-folder

Conversation

@fas89

@fas89 fas89 commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

Three follow-ups to the per-product Databricks Genie space from #863. The founder picked both designs below; these are the recommended options.

What changed

1. One space for a product deployed from several identities: extensions.databricksGenie.parentPath.

Until now, a laptop and CI each made their own space, in their own /Users/<userName> folder. That ownership rule was #863's fix against a planted decoy space. parentPath names one shared workspace folder for the space.

  • fluid apply creates the space there (parent_path).
  • It adopts only a marker space directly in that folder. The match is exact and case-sensitive; it ignores a /Workspace prefix and a trailing /, the form the API reads back.
  • A space an earlier apply made in the identity's own folder is moved in with PATCH parent_path, which keeps its id and conversations. A copy left there afterwards is trashed.
  • A space inherits its folder's permissions, so the folder's permissions become the trust boundary:
    • fluid validate refuses /Shared and anything under it. users holds CAN_MANAGE there, and everything below inherits it (measured).
    • fluid apply stops before tofu apply when the folder is missing or is not a folder.
    • It also stops when it cannot read the folder's permissions, or when users or account users holds CAN_EDIT or CAN_MANAGE on it. Lower levels are a note.
  • Removing the whole block leaves a parentPath space in place, with a note naming it. Without the block the apply no longer knows which folder to trust; retirement still looks only in the identity's own folder.

2. fluid generate iac says the module holds no Genie space. A new optional plugin hook, standalone_module_notices, prints a note under the module path, so a plain tofu apply is no surprise.

3. fluid plan and fluid diff list the space.

  • fluid plan appends one databricks_genie_space action (title, folder, tables, readers) through a new optional hook, planned_output_ports, beside converge_output_ports. It uses in-tree plugins only and skips managed streaming plans.
  • planned_objects, which fluid diff reads, lists the same action.
  • It is never drift, because nothing compares it with an apply:
    • diff's verdict for Databricks reads the live tables and the tofu state;
    • the OpenTofu engine applies from the contract;
    • emit ignores plan actions;
    • the shadow comparison counts no kind for its op.

Borrowed (receipts)

  • The /Workspace normalisation follows terraform-provider-databricks common.WorkspacePathPrefixDiffSuppress (c120719a). Here the prefix is stripped only as a whole segment.
  • This PR moves a space with PATCH parent_path rather than recreating it: the provider's dashboard parent_path is ForceNew, but the Genie API can move a space and recreating it would lose its conversations.
  • Databricks Asset Bundles' genie_spaces resource keeps state. This module stays stateless, per feat(databricks): a Genie space for a data product (extensions.databricksGenie) #863's founder call.
  • The notice seam follows AWS CDK's synth-time Annotations.addInfo.

Measured live (Free Edition, 2026-11-12, swept to 0 left)

Probe of the API:

  • A create accepts /Users/<me>, /Workspace/Users/<me>, /Shared/x, /Workspace/Shared/x, a trailing / and a root-level folder. A GET returns the bare form.
  • A missing folder is 404 and a relative path 400. Paths are case-sensitive: /shared/x is 404.
  • /Shared grants users CAN_MANAGE directly; the root grants admins only.
  • A space's permissions all come from its folder. PATCH {parent_path} moves a space.

Then the real fluid CLI against a scratch catalog and a root-level folder:

  • validate refused /Workspace/Shared/....
  • With the folder missing, the apply stopped after tofu plan, before tofu apply; no schema was made.
  • A space made in the user's folder: the dry run said it would move it, the apply moved it (same id), and a second apply changed nothing.
  • users CAN_MANAGE on the folder was refused before tofu apply; users CAN_RUN gave a note.
  • fluid plan listed genie_space_<id>, generate iac printed the note, and diff --exit-on-drift exited 0.
  • After the review fixes, a re-run of fluid plan on that contract shows the reader resolved from its {{ env.* }} template.
  • With the block removed, the space was left with a note.

Validation

  • Genie unit tests: 147 passed. Genie stand-in e2e with real tofu (offline provider mirror): 4 passed, including a new two-identity run. On the head merged with main (91430e40), across tests/iac -k databricks, tests/cli, plan, streaming, fragments, the startup budget and the agent skills: 1328 passed, 24 skipped.
  • Full non-integration suite, before the review round: 34,948 passed. The 17 failures and 2 errors are environment issues (dlt version, duckdb sandbox /var, live LLM judge, an MCP Snowflake test, two lazy-import --help tests). The re-run set fails identically on the base commit.
  • Clean: ruff, the dead-code ruff gate, black 24.10.0, product guardrail, license headers, import-linter, mypy strict hotspots, and scoped detect-secrets (the baseline shows line-number drift only).
  • ripwire --quality-delta on a fresh worktree with the branch diff: beyond the known 23-row dead-code noise baseline, only these:

Reviews

These ran per /ship, on the branch diff against 97811add. There was 1 fix round.

  • security-review (an Opus judge identified; per finding, a filter): no HIGH or MEDIUM. Two LOW hardening notes came out of it:

    • account users beside users: fixed in e4f3e97.
    • Listing the folder's other managers: a follow-up.

    A re-run on the fix round also found nothing.

  • world-class-inspection (Diff mode, delivery reviewer): PASS. Two P2 findings, both fixed in f2b5de5:

    • an AGENTS.md sentence that still stated the old rule;
    • a docstring that contradicted the code.

    The CLI UX walk over the live transcript found two minor items, also fixed: the move line now names the space, and the note for a space left behind says why.

  • defect-scan (Diff mode: logic, runtime and contract hunters, then a skeptic, then a referee). Three Medium REAL_BUGs in fluid plan's hook, fixed in a584d9b:

    • a product with a local dbt build lost the action (no contract directory);
    • {{ env.* }} templates were unresolved, so readers showed placeholders;
    • a multi-cloud contract listed the action for clouds the apply does not run.

    Three Low items were fixed in the same commit:

    • the folder check now fails closed on a permissions answer of another shape;
    • one listing on the legacy planner path;
    • a move onto a taken title gives the right fix.

    One doc gap is closed: changing or removing parentPath leaves the old space with a note.

  • Re-check: one auditor marked all 10 findings FIXED and found nothing new at Critical, High or Medium. Its two LOW notes were fixed in 8db4173: the move's 409 advice and the plan hook's docstring. The re-run security judge found no findings, and confirmed that the structure-only refactor d51a4c1 is equivalent. It moved _broadest out of the complexity row and kept the plan hook within 60 lines.

Documentation

  • Docs PR linked

  • No docs needed

  • Docs TODO: forge_docs docs/providers/databricks.md needs a parentPath part in its Genie space section. It should cover:

    • the shared folder a workspace admin creates, and who gets CAN_MANAGE on it;
    • the trust boundary: /Shared is refused, and so is a folder where users or account users can edit or manage;
    • moving a space from the identity's own folder into it;
    • the note when the folder changes or the block goes;
    • fluid plan's databricks_genie_space action and the fluid generate iac note.

    It is not written yet; the needs-docs label tracks it, as for feat(databricks): a Genie space for a data product (extensions.databricksGenie) #863.

Follow-ups

  • Print which principals, other than the deployer, can edit or manage the folder. This was LOW from the security review.
  • Make a live run with a service principal as a second deploying identity. No SP secret is available on this machine; the stand-in e2e covers the flow.
  • Add a parentPath section to the forge_docs Databricks page.

… identities

extensions.databricksGenie.parentPath names a workspace folder for the
product's Genie space, so a laptop and CI keep one space instead of one
each in their own folders. The space is created there (parent_path), and
only a space directly in that folder is adopted. One an earlier apply made
in the identity's own folder is moved in (PATCH parent_path keeps its id
and conversations), and a copy left there is trashed.

A space inherits its folder's permissions, so the folder is the trust
boundary. fluid validate refuses anything under /Shared, where the users
group holds CAN_MANAGE and every folder in it inherits that. The apply
stops before tofu apply on a folder that is missing, is not a folder, has
permissions it cannot read, or gives users CAN_EDIT or CAN_MANAGE.

Measured on a live workspace (2026-11-12):
- A create accepts /Workspace/... and a trailing slash; GET returns the
  bare form.
- A missing folder is 404 and a relative path 400; paths are
  case-sensitive.
- PATCH {parent_path} moves a space.

The /Workspace normalisation follows terraform-provider-databricks'
common.WorkspacePathPrefixDiffSuppress, but strips the prefix only as a
whole segment.
…erate iac notes it

fluid plan appends one databricks_genie_space action (title, folder,
tables, readers) through a new optional plugin hook,
planned_output_ports, beside converge_output_ports. The Databricks native
planner, which fluid diff reads for the contract's resources, lists the
same action.

Nothing compares the action with an apply, so it never reads as drift:
- diff's verdict for Databricks reads the live tables and the tofu state;
- emit ignores plan actions;
- the shadow comparison counts no kind for its op.
A managed streaming plan gets none, since its apply checks actions one
for one.

fluid generate iac prints a note, from a new optional plugin hook
standalone_module_notices, that the module holds no Genie space, so a
plain tofu apply makes none. The pattern follows AWS CDK's synth-time
Annotations.addInfo.

HONESTLY_TESTED gains the unit, stand-in and live rows for both commits.
…efused too

Under identity federation the account's all-users group, account users,
can be granted on a workspace folder. The parentPath folder check named
only the workspace's own users group, so a folder that gave account users
CAN_EDIT or CAN_MANAGE passed, and every account user could have edited
the space or read its conversations. Both groups now count as every
user, and the refusal names the group it found.
A move into parentPath now names the space by title and id, as the
trash and left-alone lines do. Removing the block leaves a space in a
parentPath folder, and the note for it now says why: without the block
the apply cannot tell that folder from any other, so the product's own
space there is to be trashed by hand.

AGENTS.md's earlier sentence now names parentPath beside the identity's
own folder. The workspace_folder docstring says the contract's value is
trimmed by its caller, while API values are compared untrimmed.
…; the folder check fails closed

The defect scan found the plan's Genie action could differ from what
the apply does:
- It saw the contract's {{ env.* }} unresolved, so a reader was a
  placeholder, or the action dropped.
- It had no contract directory, so a product with a local dbt build lost
  the action without a word.
- It listed the space for every cloud the exposes name, while the apply
  runs one plugin.

The plan hook now resolves the templates, takes the provider the apply's
_resolve_provider picks, and runs inside contract_source. A contract the
hook refuses is said as a warning with the refusal's kind. A planner that
already lists the space keeps one listing.

The parentPath folder check now fails closed on a permissions answer of
another shape: an empty or missing access control list, an all-users
entry without its levels, a level it does not know, a level in another
case (compared upper-cased), or a folder answered without its object id.
A move onto a title the folder already holds says so, with the fix,
instead of the generic credential advice.
… plan hook

Structure only, no behaviour change. _broadest now reads the entries,
and two helpers take over the rest: _held_levels parses and refuses one
all-users entry, and _unreadable builds the fail-closed error. The plan
hook's merge into plan.json stays in _append_output_port_actions, while
_output_port_actions reads the contract as the apply does. ripwire's
quality delta had the first at complexity 20 against a bar of 15, and
the second at 64 lines against 60.
…tring are accurate

A move into parentPath carries parent_path alone, so a 409 there is a
clash with the title the space has now, not the contract's. The advice
now says to rename either space, and that the apply sets the contract's
title after the move. The plan hook's docstring said it loads no
entry-point plugin, but _resolve_provider loads an operator-allowed one,
as the apply does, and the docstring now says so. Both came from the
re-check after the review round.
…nie-shared-folder

# Conflicts:
#	.secrets.baseline
#	HONESTLY_TESTED.md
@github-actions github-actions Bot added security Security-related changes or reports cli Changes to the CLI surface or implementation tests Test coverage or test infrastructure changes needs-docs Pull request needs a linked docs update or justification labels Nov 12, 2026
@github-actions

Copy link
Copy Markdown

📄 Documentation Reminder

This PR appears to be missing a documentation reference. Our docs live in a separate repo.

Please update the PR description with one of:

  • Link a docs PR — check the "Docs PR linked" box and paste the URL
  • Mark as no docs needed — check "No docs needed" with a justification
  • Acknowledge docs TODO — check "Docs TODO" and create the docs PR before merge

See the Contributing Guide for details.

@fas89
fas89 enabled auto-merge (squash) November 12, 2026 23:21
…o feat/databricks-genie-shared-folder

# Conflicts:
#	.secrets.baseline
@github-actions github-actions Bot removed the needs-docs Pull request needs a linked docs update or justification label Nov 12, 2026
@fas89
fas89 merged commit a70f2d0 into main Nov 12, 2026
52 checks passed
fas89 added a commit that referenced this pull request Nov 12, 2026
databricks_genie.py: #869's folder logic (_placed, _at_most_one, _check_folder, the move
path, _found_again(space), _title_kept(space)) with the estate's Voice, Space.token and the
read-back after every write (_keep_as_sent, on the move path's update too). #869's new
messages read the Voice: one space per {scope}, the title setting, and the parentPath hint
only where a space has that setting (Voice.folder_setting, empty for an estate).
remove_estate takes #869's _placed(...)[0]. Docs and HONESTLY_TESTED keep both sides; the
secrets baseline is main's with the line numbers refreshed.
fas89 added a commit that referenced this pull request Nov 13, 2026
…#871)

* feat(dq): read a contract's DQ rules once, as checks or refusals

fluid_build/util/dq_checks.py turns exposes[].contract.dq.rules into
engine-neutral checks (not_null, unique, accepted_values, regex, bounds,
reference, freshness, schema) and a refusal, with its fix, for each rule
no check can run as written. The allowed values of a valid_values rule
come from the selected column: its validationRules (enum 'A | B' or
'A,B', regex, range) or labels.references <product>.<column> through
consumes[]. Thresholds below 1.0 are shares computed as fluid test
computes them; critical/error map to error, warn/info to warn.

Shared with the Databricks DQ sessions (monitoring, results table,
Lakeflow expectations) as the one reading of the rules.

* feat(databricks): a dbt job runs its contract's DQ rules as dbt tests

Each check of the rules on the exposes a Databricks dbt build writes is a
singular dbt test, <test path>/fluid_dq/<model>/<check>.sql, uploaded
beside the project; the user's files upload byte for byte (dbt describes
a model in one properties entry only). dbt build runs each test after
its model, so the run page lists every rule by id and an error-severity
failure fails the run.

not_null, unique, accepted_values and relationships run dbt's built-in
test SQL through adapter.dispatch; regex, bounds, freshness, schema and
shares are forge-cli's own SQL after dbt_utils (recency,
not_null_proportion) and dbt_expectations (match_regex,
table_columns_to_match_set), both Apache-2.0, with no package to install
on serverless. Every value reaches the file as an escaped Jinja string
and every SQL literal through databricks_access.string_literal.

A rule no test can run, a Git-sourced build with rules, a model or a
referenced source the project lacks are refused at validate and plan.
The plan lists each job's tests (dq_tests); report() reads dbt's result
lines from a run's output.

* feat(dq): a reference to a product not consumed keeps no expose

A source-aligned product consumes nothing, so a labels.references to
another product's column no longer refuses in the shared reader:
Reference.expose_id is None and the engine decides whether it can find
that table (the Databricks dbt job cannot, and refuses). Two exposes of
one consumed product stay refused as ambiguous. Each refusal carries
the rule's type, so an engine that runs a type another way (Databricks
data quality monitoring for anomaly_detection) can tell its own.

* feat(databricks): the dbt job refuses an unconsumed reference; a run's report covers rules dbt stopped before

The dbt test of a reference reads the other product from the dbt source
the project declares for the expose it consumes, so a reference to a
product consumes[] does not list is refused there. report() says once
when the task's output holds no result of any rule's test (dbt stopped
before them). The job's dbt task key is a constant, DBT_TASK_KEY, which
a run's report finds the dbt output under.

* fix(databricks): the schema test refs its model while dbt parses

dbt records a test's parents while it parses, with execute false, and
dbt build --select +model+ selects tests by them. The schema test called
ref() only inside its execute guard, so it had no parent and was never
selected: live on Databricks Free Edition dbt found 11 tests and ran 10.
The relation is now set before the guard. Every kind is pinned to ref
its model at parse time.

* docs(databricks): changelog entry and the live row for DQ rules as dbt tests

The Unreleased entry says what runs for each rule type, what is refused,
and what fluid apply prints. HONESTLY_TESTED records the unit tiers and
the manual live run on Databricks Free Edition (2026-11-12): a seeded
bad row failed the run on its two error rules and warned on two, a clean
run passed all 11 tests, the next plan was empty, and everything was
swept; it also lists what was not run live.

* refactor(dq): smaller units in the rule reader and the test compiler; two hardening fixes

From the branch's security review and ripwire --quality-delta:

- The schema test escapes the expected type as it escapes the type the
  warehouse reports. It is unreachable today (every Databricks column
  type passes _column_type first), but no longer depends on it (LOW).
- A reference refusal quotes the labels.references parts it names (LOW).
- compile_tests runs in stages (a _Project with its paths and lazily
  read sources, _test_folder, _add_test); _declared_sources reads
  through _properties_docs and _source_tables; _Rule._allowed reads each
  validationRule kind in its own method; read_rules and has_rules share
  _parts; report's wording is _outcome.
- The job's tests ride on _Source, so _emit_job keeps its signature and
  its task dict stays one line.

* feat(dq): each check keeps its rule's severity, threshold and operator as written

DqCheck.rule_severity, rule_threshold and rule_operator carry the rule
as the contract wrote it, beside the two-level severity a dbt test runs
at and the share a lower threshold becomes: Lakeflow expectations fail
an update on critical and drop the row on error, and a results table
reports one row per rule.

* fix(databricks): refuse a test name the project uses and a selection that skips the model

From the diff's delivery review:

- A rule's test is named after its id, and dbt names a singular test once
  per project: a project test of the same name made the whole job fail to
  parse ("dbt found two data_tests with the name"). It is refused at plan.
- A build whose dbt selection provably leaves the model out would never
  run its tests: refused, with the model's fqn and path evaluated by
  databricks_retention.selects_model. A tag, config or state selection
  cannot be told here; the run's report names any test that did not run.
- A real dbt (dbt-core with dbt-duckdb, as CI installs) parses the upload
  and dbt ls --select +orders+ lists every rule's test; with the schema
  test's ref put back behind its execute guard, it fails.
- Wording: a share is fluid test's own only for completeness and
  uniqueness; the anomaly and drift refusal says what fluid test does
  with them; dq_checks is the reading for the engines that run the rules,
  this job its first.

* feat(databricks): fluid apply reports what dbt found for each contract rule after a run

Under --mode amend-and-build, once the job's run ends (success or
failure), the run report reads the dbt task's output and prints one line
per contract rule's test: pass, warn or fail with its failing-row count,
error, skip, or not in the output (with the note when Databricks kept only
its end), then the summary. The rules come from the contract alone
(databricks_dbt_tests.expected_tests); the task is found by DBT_TASK_KEY,
so the fluid_keys task beside it is never read as dbt's.

runs/get-output is read once per task and feeds the dbt summary, the
failed task's tail and the rules alike (_task_output). A rule report is
printed for the dbt task when a later task failed too: its tests ran.
Never fails a build: an unreadable output or contract only drops the
report.

* fix(dq): refuse a threshold or bound no float holds, and a range no value meets

From the diff's defect scan: an integer threshold of hundreds of digits
raised OverflowError out of fluid validate and plan, and so did a window
too long to count; an infinite or NaN threshold, or a range bound past a
float's range, reached the SQL as a bare 'inf'. Each is now a refusal
(_finite, and parse_range drops a non-finite bound). A range no value
can meet (between 100 and 0, > 5 and < 1, > 3 and <= 3) is refused
instead of failing every row of its test.

* fix(databricks): defect-scan fixes to the DQ tests and the keys task

- Only the exposes with rules are tested, so only their models are looked
  for: a sibling output with no rules (no model of its name, or left out
  of the selection) was refused.
- properties.model names the model of the one table a build writes; a
  build that writes several (no outputs writes every expose) tests each
  through the model named after its table.
- fluid validate, which reads the contract before {{ env.* }} is
  substituted, no longer refuses a model named by a placeholder; the
  apply writes those tests.
- The schema test spells a decimal as the table emitter does
  (_canonical_type): decimal(12) is decimal(12,0), as Databricks reports.
- _jinja_str keeps characters outside the BMP whole: ensure_ascii wrote
  a surrogate pair, which dbt cannot encode (one emoji in a rule id
  failed the whole project's parse).
- The test-name clash check also covers the project's own files under
  fluid_dq/, and a project YAML nested past the recursion limit is passed
  over like any file that does not parse.
- The fluid_keys task (#860) runs with run_if ALL_DONE: a failing rule
  fails dbt after its model replaced the table, which dropped its keys.

* docs(databricks): AGENTS.md and the live row for the DQ tests and their report

AGENTS.md says the job runs the contract's DQ rules as dbt tests and
where the reader and the renderer live; the changelog says the keys task
now runs whatever dbt's outcome; HONESTLY_TESTED counts the tests again
(48 + 54 + 4) and lists what the review waves added.

* feat(databricks): an anomaly_detection rule is left to data quality monitoring

#864 turns Databricks data quality monitoring on for an expose with an
anomaly_detection rule, and the monitor learns the rule's bounds, which
a dbt test cannot. The job's tests therefore leave such a rule to it
(databricks_quality.monitoring_request), as agreed with that change.
An expose that turns monitoring off (contract.dq.monitoring.enabled:
false) still has its anomaly rule refused, and the fix names the
opt-out. drift_detection stays refused.

* fix(databricks): refuse an unpaired surrogate; a placeholder selection waits for the apply

From the fix wave's re-check:

- Since the Jinja strings keep raw Unicode, an unpaired surrogate (YAML's
  "\ud83d" escape loads one) in a rule id, an enum value or a regex
  raised UnicodeEncodeError out of fluid plan. The reader refuses one with
  the control characters, in ids too, and a value from the project's own
  YAML (a source name) that UTF-8 cannot hold is a refusal of its own.
- fluid validate, before {{ env.* }} is substituted, refused a build whose
  properties.select still held a placeholder as leaving the model out; the
  selection is judged only once it can be read, at the apply.

* docs(databricks): the anomaly hand-off to monitoring, and the counts, in the changelog and the live row

* fix(databricks): refuse a test path no file can hold; monitoring takes over only the history refusal

From the round-2 re-check:

- An unpaired surrogate in dbt_project.yml's first test path (YAML's
  "\ud83d" escape loads one) reached the generated tests' paths and raised
  UnicodeEncodeError out of fluid plan. It is a test-path refusal now, and
  the message names the path escaped.
- On an expose Databricks data quality monitoring watches, every refusal of
  an anomaly_detection rule was dropped, so a bad severity or an id holding
  a control character passed silently. DqRefusal.needs_history marks the
  one refusal monitoring takes over (the rule needs the table's history);
  the others are refused as before.

* fix(databricks): a DQ refusal names a lone surrogate from the project escaped

The round-3 re-check found model-paths reaching the model refusal's text
raw, and a lone surrogate there (a YAML escape loads one) made fluid plan
fail on printing the refusal. Every DQ refusal is built in _refusal, which
now spells such a character as its backslash escape; a source or table
name from the project's YAML in a reference refusal is covered the same way.

* docs(databricks): the live row counts the round-3 tests and names the escaped refusal text

* fix(dq): a valid_values rule allows what fluid test allows

#867 landed fluid test's reading of a valid_values rule,
providers/quality_engine.py::value_domain: the values the rule itself
lists, else every declaration its column makes (the first enum, regex and
range validationRule, and labels.references), all of which must hold.
dq_checks had its own, which let the column's declarations win over the
rule's list, split an enum on | only when it held one, and read every
validationRule of a kind; the dbt tests could then check other values than
fluid test does.

dq_checks now takes the values, pattern and bounds from value_domain and
keeps its refusals on top: a range fluid test cannot read (it passes one
over) or no value can meet, a value or pattern holding a control
character, and a labels.references that consumes[] cannot resolve. Its own
parse_enum and parse_range are gone; the range grammar is quality_engine's.
The Tower Management pack still reads as 330 checks with none refused,
check for check as before, and the demo's dbt products as 230 tests.

* docs(dq): valid_values reads as fluid test's value_domain, in AGENTS.md, the changelog and the live row

* chore(secrets): baseline line numbers after the rebase on #865 and #869

* test(databricks): the schema test's expected rows join their parts with an explicit +
@fas89 fas89 mentioned this pull request Nov 13, 2026
1 task done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Changes to the CLI surface or implementation security Security-related changes or reports tests Test coverage or test infrastructure changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant